use super::{Attr, JoinHandle, Runtime};
use crate::{Error, Result};
use core::future::Future;

/// 当前调用点临时创建异步运行时实例. 阻塞当前线程，入口异步任务结束时才返回,
/// 异步函数内部调用runtime::task::spawn系列创建的异步任务都在此临时异步运行时中被调度.
/// 可通过Attr::max_cache指定此临时运行时支持task cache的最大值，缺省为0，不支持.
pub fn block_on_with<T>(future: T, attr: &Attr) -> Result<T::Output>
where
    T: Future,
{
    if let Ok(task) = Runtime::local_spawn(future, attr) {
        JoinHandle::<T::Output>::new(task).join()
    } else {
        Err(Error::default())
    }
}

/// 参见block_on_with, 参数attr对应为Attr::default()
pub fn block_on<T>(future: T) -> Result<T::Output>
where
    T: Future,
{
    block_on_with(future, &Attr::default())
}

#[cfg(test)]
mod test {
    extern crate std;
    use crate::runtime::*;
    use core::time::Duration;
    #[test]
    fn test_future() {
        async fn test_foo() -> i32 {
            100
        }
        let val = local::block_on(test_foo()).unwrap();
        assert_eq!(val, 100);
    }

    #[test]
    fn test_task_spawn() {
        async fn test_foo() -> i32 {
            100
        }
        async fn test_bar() -> i32 {
            task::spawn_local(test_foo()).await.await.unwrap()
                + local::block_on(test_foo()).unwrap()
        }

        let val = local::block_on(test_bar()).unwrap();
        assert_eq!(val, 200);
    }

    #[test]
    fn test_sleep() {
        async fn test_sleep(val: i32) -> i32 {
            sleep(core::time::Duration::new(1, 0)).await;
            yield_now().await;
            val + 100
        }
        let val = local::block_on(test_sleep(100)).unwrap();
        assert_eq!(val, 200);
    }

    #[test]
    fn test_ready() {
        let val = local::block_on(async {
            let mut retn: i32 = 0;
            async { 2_i32 }.ready(|result: i32| retn = result).await;
            retn
        })
        .unwrap();
        assert_eq!(val, 2);
    }

    #[test]
    fn test_select_any() {
        let (r1, r2) = local::block_on(async {
            let mut r1: i32 = 0;
            let mut r2: i32 = 0;
            let t1 = async { 1_i32 }.ready(|result: i32| r1 = result);
            let t2 = async { 2_i32 }.ready(|result: i32| r2 = result);
            t1.or(t2).await;
            (r1, r2)
        })
        .unwrap();
        assert_eq!(r1, 1);
        assert_eq!(r2, 0);
    }

    #[test]
    fn test_select_all() {
        let (r1, r2) = local::block_on(async {
            let mut r1: i32 = 0;
            let mut r2: i32 = 0;
            let t1 = async { 1_i32 }.ready(|result: i32| r1 = result);
            let t2 = async { 2_i32 }.ready(|result: i32| r2 = result);
            t1.and(t2).await;
            (r1, r2)
        })
        .unwrap();
        assert_eq!(r1, 1);
        assert_eq!(r2, 2);
    }

    #[test]
    fn test_or_ready() {
        let (r1, r2, r3) = local::block_on(async {
            async fn foo() -> i32 {
                1
            }
            async fn bar() -> &'static str {
                "hello"
            }
            async fn baz() -> i32 {
                2
            }
            let mut foo_retn = 0;
            let mut bar_retn = "";
            let mut baz_retn = 0;
            foo()
                .ready(|val| foo_retn = val)
                .or(bar().ready(|val| bar_retn = val))
                .or(baz().ready(|val| baz_retn = val))
                .await;
            (foo_retn, bar_retn, baz_retn)
        })
        .unwrap();
        assert_eq!(r1, 1);
        assert_eq!(r2, "");
        assert_eq!(r3, 0);
    }
    #[test]
    fn test_and_ready() {
        let (r1, r2, r3) = local::block_on(async {
            async fn foo() -> i32 {
                1
            }
            async fn bar() -> &'static str {
                "hello"
            }
            async fn baz() -> i32 {
                2
            }
            let mut foo_retn = 0;
            let mut bar_retn = "";
            let mut baz_retn = 0;
            foo()
                .ready(|val| foo_retn = val)
                .and(bar().ready(|val| bar_retn = val))
                .or(baz().ready(|val| baz_retn = val))
                .await;
            (foo_retn, bar_retn, baz_retn)
        })
        .unwrap();
        assert_eq!(r1, 1);
        assert_eq!(r2, "hello");
        assert_eq!(r3, 0);
    }

    #[test]
    fn test_deadline() {
        let beg = crate::time::now();
        async fn foo() -> Option<i32> {
            async fn fun() -> i32 {
                sleep(Duration::new(3, 0)).await;
                return 1;
            }
            fun().deadline(Duration::new(1, 0)).await
        }
        let x = local::block_on(foo()).unwrap();
        assert_eq!(None, x);
        let end = crate::time::now();
        let elapse = end - beg;
        assert!(elapse < Duration::new(2, 1000));
    }
}
